        title   "Software Interrupts"
;++
;
;Copyright (c) 1992, 1993, 1994  Corollary Inc
;
;Module Name:
;
;    cbswint.asm
;
;Abstract:
;
;    This module implements the HAL software interrupt routines
;    for the MP Corollary implementation under Windows NT.
;
;Author:
;
;    Landy Wang (landy@corollary.com) 26-Mar-1992
;
;Environment:
;
;    Kernel Mode
;
;Revision History:
;
;--


.386p
        .xlist
include halx86.inc
include callconv.inc                    ; calling convention macros
include x86\kimacro.inc
include x86\cbus.inc
include macx86.inc

        EXTRNP  _HalBeginSystemInterrupt,3
        EXTRNP  _HalEndSystemInterrupt,2

        .list

_TEXT   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;++
;
; VOID
; FASTCALL
; HalRequestSoftwareInterrupt (
;    IN KIRQL RequestIrql
;    )
;
; Routine Description:
;
;    This routine is used to issue a software interrupt to the
;    calling processor.  Since this is all done in hardware, the
;    code to implement this is trivial.  Our hardware supports
;    sending the interrupt to lowest-in-group processors, which
;    would be useful for a good number of DPCs, for example, but
;    the kernel doesn't currently tell us which kinds of software
;    interrupts need to go to the caller versus which can go to
;    any processor.
;
; Arguments:
;
;    (cl) = RequestIrql - Supplies the request IRQL value
;
; Return Value:
;
;    None.
;
;--

cPublicFastCall HalRequestSoftwareInterrupt   ,1

        push    ecx
        call    dword ptr [_CbusRequestSoftwareInterrupt]
        fstRet  HalRequestSoftwareInterrupt

fstENDP HalRequestSoftwareInterrupt

;++
;
; VOID
; HalClearSoftwareInterrupt (
;    IN KIRQL RequestIrql
;    )
;
; Routine Description:
;
;   This routine is used to clear a possible pending software interrupt.
;   Support for this function is optional, and allows the kernel to
;   reduce the number of spurious software interrupts it receives.  Since
;   neither the APIC nor the CBC can clear an interrupt once it is sent,
;   this optional function becomes a no-op in the Cbus HAL.
;
; Arguments:
;
;    (cl) = RequestIrql - Supplies the request IRQL value
;
; Return Value:
;
;    None.
;
;--

cPublicFastCall HalClearSoftwareInterrupt ,1
        fstRET  HalClearSoftwareInterrupt
fstENDP HalClearSoftwareInterrupt

        page ,132
        subttl  "Dispatch Interrupt"
;++
;
; VOID
; HalpDispatchInterrupt(
;       VOID
;       );
;
; Routine Description:
;
;    This routine is the interrupt handler for a software interrupt generated
;    at DISPATCH_LEVEL.  Its function is to save the machine state, raise
;    Irql to DISPATCH_LEVEL, dismiss the interrupt, and call the DPC
;    delivery routine.
;
;    Note that the "software" interrupt has in fact been
;    generated by software, but delivered by hardware - thus, no iret
;    frame needs to be constructed here by software.
;
; Arguments:
;
;    None
;    Interrupt is disabled
;
; Return Value:
;
;    None.
;
;--

        ENTER_DR_ASSIST hdpi_a, hdpi_t

cPublicProc _HalpDispatchInterrupt   ,0
;
; Save machine state on trap frame
;

        ENTER_INTERRUPT hdpi_a, hdpi_t

	;
	; The only thing we need to save here is the interrupted taskpri.
	; We must EOI the APIC immediately as there is no interrupt source,
	; and if we context switch and exit this thread, then we may never EOI!
	; the above macro will save all our registers, so we don't need to
	; below.  Thus, the EOI serves as the HalEndSystemInterrupt.
	;

	mov     eax, DPC_TASKPRI                        ; mark interrupting vec
	CBUS_EOI eax, ecx				; destroy eax & ecx

        mov     esi, dword ptr PCR[PcHal.PcrTaskpri]	; get h/w taskpri addr

	push	dword ptr [esi]				; save entry taskpri
if DBG
        cmp     dword ptr [esi], 0                      ; old irql not zero?
        je      short irqlok
        cmp     dword ptr [esi], 01fh                   ; old irql not zero?
        je      short irqlok
        int     3
irqlok:
endif

        mov     dword ptr [esi], DPC_TASKPRI		; set new h/w taskpri
	sti						; and allow interrupts

	;
	; Go do Dispatch Interrupt processing - we may context switch away from
	; this thread here, resume the idle thread and either much later (or
	; never) continue onward.  so we must EOI _before_ dispatching with
        ; this call.
	;

        stdCall   _KiDispatchInterrupt

	;
	; restore our original IRQL by programming it into the interrupt
	; controller since we will read it out of the interrupt controller
	; on the next KfRaiseIrql().  We must re-read the address of the
	; task priority register for the current processor before setting
	; it because we may be resuming a thread on a different processor
	; from the one that originally entered this routine.  For the APIC,
	; this wouldn't matter because the task priority register is at the
	; same physical address for all processors.  But for the CBC, each
	; task priority lies in global physical space (at different addresses).
	; since we are at DISPATCH_LEVEL here, we cannot be pre-empted as
	; we do this.
	;

ifdef CBC_REV1
	pop	eax				; get entry taskpri
if DBG
        cmp     eax, 0                      ; old irql not zero?
        je      short irqlok2
        cmp     eax, 01fh                   ; old irql not zero?
        je      short irqlok2
        int     3
irqlok2:
endif
        pushfd
        cli
        mov     esi, dword ptr PCR[PcHal.PcrTaskpri]
	mov	dword ptr [esi], eax		; restore entry taskpri
	popfd
else
        mov     esi, dword ptr PCR[PcHal.PcrTaskpri]
	pop	dword ptr [esi]				; restore entry taskpri
endif

	;
	; Call this directly instead of through INTERRUPT_EXIT
	; because the HalEndSystemInterrupt has already been done,
	; and must only be done ONCE per interrupt.
	;

        cli
        SPURIOUS_INTERRUPT_EXIT       ; exit interrupt without EOI

stdENDP _HalpDispatchInterrupt

        page ,132
        subttl  "APC Interrupt"
;++
;
; HalpApcInterrupt(
;       VOID
;       );
;
; Routine Description:
;
;    This routine is entered as the result of a software interrupt generated
;    at APC_LEVEL. Its function is to save the machine state, raise Irql to
;    APC_LEVEL, dismiss the interrupt, and call the APC delivery routine.
;
;    Note that the "software" interrupt has in fact been
;    generated by software, but delivered by hardware - thus, no iret
;    frame needs to be constructed here by software.
;
; Arguments:
;
;    None
;    Interrupt is Disabled
;
; Return Value:
;
;    None.
;
;--

        ENTER_DR_ASSIST hapc_a, hapc_t

cPublicProc _HalpApcInterrupt   ,0

;
; Save machine state in trap frame
;
        ENTER_INTERRUPT hapc_a, hapc_t

	; The only thing we need to save here is the interrupted taskpri.
	; We must EOI the APIC immediately as there is no interrupt source,
	; and if we context switch and exit this thread, then we may never EOI!
	; the above macro will save all our registers, so we don't need to
	; below.  Thus, the EOI serves as the HalEndSystemInterrupt.

	mov     eax, APC_TASKPRI                        ; mark interrupting vec
	CBUS_EOI eax, ecx				; destroy eax & ecx

        mov     esi, dword ptr PCR[PcHal.PcrTaskpri]	; get h/w taskpri addr

	push	dword ptr [esi]				; save entry taskpri

if DBG
        cmp     dword ptr [esi], 0                      ; old irql not zero?
        je      short @f
        int     3
@@:
endif

        mov     dword ptr [esi], APC_TASKPRI		; set new h/w taskpri
	sti						; and allow interrupts

	; call the APC delivery routine(previous mode,Null exception frame,
	;					trap frame)


        mov     eax, [ebp]+TsSegCs      ; get interrupted code's CS
        and     eax, MODE_MASK          ; extract the mode

	stdCall	_KiDeliverApc, <eax, 0, ebp>

	;
	; restore our original IRQL by programming it into the interrupt
	; controller since we will read it out of the interrupt controller
	; on the next KfRaiseIrql().  We must re-read the address of the
	; task priority register for the current processor before setting
	; it because we may be resuming a thread on a different processor
	; from the one that originally entered this routine.  For the APIC,
	; this wouldn't matter because the task priority register is at the
	; same physical address for all processors.  But for the CBC, each
	; task priority lies in global physical space (at different addresses).
	; since we are at APC_LEVEL here, we must protect against a context
	; switch happening between reading the taskpri address and actually
	; setting its value, hence the cli...
	;

ifdef CBC_REV1
        cli
endif
if DBG
        cmp     dword ptr [esp], 0                      ; old irql not zero?
        je      short @f
        int     3
@@:
endif
        mov     esi, dword ptr PCR[PcHal.PcrTaskpri]
	pop	dword ptr [esi]				; restore entry taskpri

if DBG
        cmp     dword ptr [esi], 0                      ; old irql not zero?
        je      short @f
        int     3
@@:
endif
	;
	; Call this directly instead of through INTERRUPT_EXIT
	; because the HalEndSystemInterrupt has already been done,
	; and must only be done ONCE per interrupt.
	;

        cli
        SPURIOUS_INTERRUPT_EXIT     ; exit interrupt without eoi

stdENDP _HalpApcInterrupt


        page ,132
        subttl  "HalRequestIpi"
;++
;
; VOID
; HalRequestIpi(
;       IN ULONG Mask
;       );
;
; Routine Description:
;
;    Requests an interprocessor interrupt
;
;    for Windows NT (and MPX 2.1), we use full distributed
;    interrupt capability, and, thus, we will IGNORE the sswi address
;    that RRD passes us and prioritize IPI as we see fit, given the
;    other devices configured into the system.  see the backend Cbus1
;    and Cbus2 handlers for more details.
;
; Arguments:
;
;    Mask - Mask of processors to be interrupted
;
; Return Value:
;
;    None.
;
;--

cPublicProc _HalRequestIpi   ,1

        jmp	dword ptr [_CbusRequestIPI]

stdENDP _HalRequestIpi

;++
;
; VOID
; HalpIpiHandler (
;    );
;
; Routine Description:
;
;    This routine is entered as the result of an interrupt generated by inter
;    processor communication. Its function is to call its handler.
;
; Arguments:
;
;    None.
;    Interrupt is dismissed
;
; Return Value:
;
;    None.
;
;--

        ENTER_DR_ASSIST Hipi_a, Hipi_t

	align 4

cPublicProc _HalpIpiHandler       ,0

;
; Save machine state in trap frame
;

        ENTER_INTERRUPT Hipi_a, Hipi_t	    ; (ebp) -> Trap frame

	;
	; Save interrupting vector and previous IRQL.  previous IRQL will
	; be filled in by our call to HalBeginSystemInterrupt, and the
	; stack space for both will be removed via INTERRUPT_EXIT's
	; call to HalEndSystemInterrupt.
	;
	push	dword ptr [_CbusIpiVector]
        sub     esp, 4                      ; space for OldIrql

	;
	; We now dismiss the interprocessor interrupt
	;	(Irql, Vector, stack location of OldIrql)

        stdCall	_HalBeginSystemInterrupt, <IPI_LEVEL, dword ptr [_CbusIpiVector], esp>

	; Pass Null ExceptionFrame
	; Pass TrapFrame to Ipi service rtn

        stdCall	_KiIpiServiceRoutine, <ebp, 0>

	;
	; Do interrupt exit processing
	;

        INTERRUPT_EXIT                      ; will return to caller

stdENDP _HalpIpiHandler


_TEXT   ENDS
        END
